package hys.wssoc.modules.info.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import hys.wssoc.common.utils.PageUtils;
import hys.wssoc.common.utils.Query;
import hys.wssoc.config.ElasticSearchConfig;
import hys.wssoc.modules.constant.RedisConstants;
import hys.wssoc.modules.info.dao.CountrysideDao;
import hys.wssoc.modules.info.entity.CountrysideEntity;
import hys.wssoc.modules.info.service.CountrysideService;
import hys.wssoc.modules.info.service.IRedisService;
import hys.wssoc.modules.info.utils.CommonUtils;
import hys.wssoc.modules.info.vo.CountrysideAggVo;
import hys.wssoc.modules.info.vo.CountrysideDetailVo;
import hys.wssoc.modules.info.vo.CountrysideSummaryVo;
import hys.wssoc.modules.info.vo.DataListByTimeVO;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service("countrysideService")
public class CountrysideServiceImpl extends ServiceImpl<CountrysideDao, CountrysideEntity> implements CountrysideService {
    @Autowired
    private CountrysideDao countrysideDao;

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private IRedisService redisService;

    @Override
    public List<DataListByTimeVO> detailListByTime(Integer rsTime) {
        List<CountrysideDetailVo> dataListByTime;
        if (redisService.exists(RedisConstants.COUNTRYSIDE + "detailList_" + rsTime)) {
            String redisValue = redisService.get(RedisConstants.COUNTRYSIDE + "detailList_" + rsTime);
            dataListByTime = JSON.parseArray(redisValue, CountrysideDetailVo.class);
        } else {
            QueryWrapper<CountrysideEntity> wrapper = new QueryWrapper<>();
            wrapper.eq("rs_time", rsTime);
            wrapper.select("countryside_name", "province_name", "city_name");
            dataListByTime = CommonUtils.transformClass4List(countrysideDao.selectList(wrapper), CountrysideDetailVo.class);
            redisService.set(RedisConstants.COUNTRYSIDE + "detailList_" + rsTime, JSON.toJSONString(dataListByTime), 86400L);
        }
        return integrateData(dataListByTime, rsTime);
    }

    // 进行查询页面的树状图展示
    public List<DataListByTimeVO> integrateData(List<CountrysideDetailVo> dataListByTime, Integer rsTime){
        Map<String, Map<String, List<CountrysideDetailVo>>> groupedData = dataListByTime.stream()
                .collect(Collectors.groupingBy(CountrysideDetailVo::getProvinceName,
                        Collectors.groupingBy(CountrysideDetailVo::getCityName)));
        List<DataListByTimeVO> result;
        // 从redis中获取，若存在，直接返回数据
        if (redisService.exists(RedisConstants.COUNTRYSIDE + "integrateData_" + rsTime)) {
            String redisValue = redisService.get(RedisConstants.COUNTRYSIDE + "integrateData_" + rsTime);
            result = JSON.parseArray(redisValue, DataListByTimeVO.class);
        } else {
            result = groupedData.entrySet().stream()
                    .map(provinceEntry -> {
                        DataListByTimeVO provinceData = new DataListByTimeVO();
                        provinceData.setLabel(provinceEntry.getKey());
                        provinceData.setExpanded(false);

                        List<DataListByTimeVO> cityDataList = provinceEntry.getValue().entrySet().stream()
                                .map(cityEntry -> {
                                    DataListByTimeVO cityData = new DataListByTimeVO();
                                    cityData.setLabel(cityEntry.getKey());
                                    cityData.setExpanded(false);

                                    List<DataListByTimeVO> countrysideDataList = cityEntry.getValue().stream()
                                            .map(countryside -> {
                                                DataListByTimeVO countrysideData = new DataListByTimeVO();
                                                countrysideData.setLabel(countryside.getCountrysideName());
                                                countrysideData.setExpanded(false);
                                                return countrysideData;
                                            }).collect(Collectors.toList());

                                    cityData.setChildren(countrysideDataList);
                                    return cityData;
                                }).collect(Collectors.toList());

                        provinceData.setChildren(cityDataList);
                        return provinceData;
                    }).collect(Collectors.toList());
            redisService.set(RedisConstants.COUNTRYSIDE + "integrateData_" + rsTime, JSON.toJSONString(result), 86400L);
        }
        return result;
    }


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CountrysideEntity> page = this.page(
                new Query<CountrysideEntity>().getPage(params),
                new QueryWrapper<CountrysideEntity>()
        );
        return new PageUtils(page);
    }

    @Override
    public List<CountrysideSummaryVo> summaryList(String cityName) {
        List<CountrysideEntity> summaryList;
        if (redisService.exists(RedisConstants.COUNTRYSIDE + "summaryList_" + cityName)) {
            String redisValue = redisService.get(RedisConstants.COUNTRYSIDE + "summaryList_" + cityName);
            summaryList = JSON.parseArray(redisValue, CountrysideEntity.class);
        } else {
            QueryWrapper<CountrysideEntity> wrapper = new QueryWrapper<>();
            wrapper.select("countryside_name", "rs_time");
            wrapper.eq("city_name", cityName);
            summaryList = countrysideDao.selectList(wrapper);
            redisService.set(RedisConstants.COUNTRYSIDE + "summaryList_" + cityName, JSON.toJSONString(summaryList), 86400L);
        }
        // 将数据库返回值赋值给VO对象返回给Controller层

        return summaryList.size() == 0 ? new ArrayList<>() :
                CommonUtils.transformClass4List(summaryList, CountrysideSummaryVo.class);
    }


    @Override
    public List<CountrysideAggVo> getAggDataByTime(Integer rsTime) throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("countryside");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery("rsTime", rsTime.toString()));
        sourceBuilder.aggregation(AggregationBuilders.terms("byTime-byProvince").field("provinceName.keyword").size(35)
                .subAggregation(AggregationBuilders.terms("byProvince-byCity").field("cityName.keyword").size(50))
        );

        searchRequest.source(sourceBuilder);
        SearchResponse response = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);

        Terms aggregations = response.getAggregations().get("byTime-byProvince");

        return aggregations.getBuckets().stream().map(bucket1 -> {
            CountrysideAggVo agg1Vo = CountrysideAggVo.builder().name(bucket1.getKeyAsString()).value(bucket1.getDocCount()).build();
            List<String> ucCity = Arrays.asList("北京", "天津", "上海", "重庆");
            if (ucCity.contains(bucket1.getKeyAsString())) {
                agg1Vo.setChildren(null);
            } else {
                Terms aggregations2 = bucket1.getAggregations().get("byProvince-byCity");
                agg1Vo.setChildren(aggregations2.getBuckets().stream().map(
                        bucket2 -> CountrysideAggVo.builder().name(bucket2.getKeyAsString()).value(bucket2.getDocCount()).build()).collect(Collectors.toList())
                );
            }
            return agg1Vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<CountrysideAggVo> getAggDataByGeo() throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("countryside");

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        sourceBuilder.aggregation(AggregationBuilders.terms("byProvince").field("provinceName.keyword").size(35)
                .subAggregation(AggregationBuilders.terms("byProvince-byCity").field("cityName.keyword").size(21)
                        .subAggregation(AggregationBuilders.terms("byCity-byCountryside").field("countrysideName.keyword").size(38)))
        );

        searchRequest.source(sourceBuilder);
        SearchResponse response = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);

        Terms aggregations = response.getAggregations().get("byProvince");

        return aggregations.getBuckets().stream().map(bucket1 -> {
            CountrysideAggVo agg1Vo = CountrysideAggVo.builder().name(bucket1.getKeyAsString()).value(bucket1.getDocCount()).build();
            Terms aggregations2 = bucket1.getAggregations().get("byProvince-byCity");

            agg1Vo.setChildren(aggregations2.getBuckets().stream().map(bucket2 -> {
                CountrysideAggVo agg2Vo = CountrysideAggVo.builder().name(bucket2.getKeyAsString()).value(bucket2.getDocCount()).build();
                Terms aggregations3 = bucket2.getAggregations().get("byCity-byCountryside");

                agg2Vo.setChildren(aggregations3.getBuckets().stream().map(
                        bucket3 -> CountrysideAggVo.builder().name(bucket3.getKeyAsString()).value(bucket3.getDocCount()).build()).collect(Collectors.toList())
                );

                return agg2Vo;
            }).collect(Collectors.toList()));

            return agg1Vo;
        }).collect(Collectors.toList());
    }
}